// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TransactionsViewModel.cs" company="Concept Architecture">
//   (c) 2010 Muhammad Umer Farooq
// </copyright>
// <summary>
//   Defines the <seealso cref="TransactionsViewModel" /> type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Hisaab.Client.Modules.Core.Transaction.ViewModel
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.Contracts;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Input;

    using Hisaab.Business.Interfaces;
    using Hisaab.Client.Common;
    using Hisaab.Client.Common.BaseClasses;
    using Hisaab.Client.Common.Services;
    using Hisaab.Client.Model;
    using Hisaab.Common;

    using Microsoft.Practices.Prism;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.Regions;
    using Microsoft.Practices.ServiceLocation;

    /// <summary>
    /// Defines the <seealso cref="TransactionsViewModel"/> type.
    /// </summary>
    public class TransactionsViewModel : TabViewModel, INavigationAware
    {
        /// <summary>
        /// Account object
        /// </summary>
        private readonly ITransactionManager _transactionManager;

        /// <summary>
        /// Member variable _accountManager
        /// </summary>
        private readonly IAccountManager _accountManager;

        /// <summary>
        /// Selected transaction
        /// </summary>
        private Transaction _selectedTransaction;

        /// <summary>
        /// Transactions list
        /// </summary>
        private ObservableCollection<Transaction> _transactions;

        /// <summary>
        /// Transactions list
        /// </summary>
        private ObservableCollection<KeyValuePair<int, decimal>> _expanceChartData;

        /// <summary>
        /// Transactions list
        /// </summary>
        private ObservableCollection<KeyValuePair<TransactionTypes, decimal>> _comparisonChartData;

        /// <summary>
        /// Order records by flag
        /// </summary>
        private OrderByType _sortOrder = OrderByType.Ascending;

        /// <summary>
        /// Filter by flag
        /// </summary>
        private SortByType _sortBy = SortByType.Date;

        /// <summary>
        /// Filter by account id
        /// </summary>
        private int _filterAccountId;

        /// <summary>
        /// Filter by month
        /// </summary>
        private int _filterMonth;

        /// <summary>
        /// Member variable _showIncome
        /// </summary>
        private bool _showIncome;

        /// <summary>
        /// Member variable _showExpanse
        /// </summary>
        private bool _showExpanse;

        /// <summary>
        /// Member variable _allowChangeAccount
        /// </summary>
        private bool _allowChangeAccount;

        /// <summary>
        /// Member variable _refreshCommand
        /// </summary>
        private ICommand _refreshCommand;

        /// <summary>
        /// Member variable _editCommand
        /// </summary>
        private ICommand _editCommand;

        /// <summary>
        /// Member variable _deleteCommand
        /// </summary>
        private ICommand _deleteCommand;

        /// <summary>
        /// Member variable _filterTagId
        /// </summary>
        private int _filterTagId;

        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionsViewModel"/> class.
        /// </summary>
        public TransactionsViewModel()
            : this(ServiceLocator.Current.GetInstance<ITransactionManager>(), ServiceLocator.Current.GetInstance<IAccountManager>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TransactionsViewModel"/> class.
        /// </summary>
        /// <param name="transactionManager">The transaction manager.</param>
        /// <param name="accountManager">The account manager.</param>
        public TransactionsViewModel(ITransactionManager transactionManager, IAccountManager accountManager)
        {
            Contract.Requires<ArgumentNullException>(transactionManager != null);
            Contract.Requires<ArgumentNullException>(accountManager != null);

            _transactionManager = transactionManager;
            _accountManager = accountManager;
            IsActiveChanged += OnActiveChanged;
            ShowExpanse = true;
            ShowIncome = true;
            SelectedQueryAccount = -1;

            GlobalCommands.Refresh.RegisterCommand(RefreshCommand);
        }

        /// <summary>
        /// Gets the refresh command.
        /// </summary>
        public ICommand RefreshCommand
        {
            get
            {
                if (_refreshCommand == null)
                {
                    _refreshCommand = new DelegateCommand(RefreshList);
                }

                return _refreshCommand;
            }
        }

        /// <summary>
        /// Gets the edit command.
        /// </summary>
        public ICommand EditCommand
        {
            get
            {
                if (_editCommand == null)
                {
                    _editCommand = new DelegateCommand(EditTransaction, CanEditTransaction);
                }

                return _editCommand;
            }
        }

        /// <summary>
        /// Gets the delete command.
        /// </summary>
        public ICommand DeleteCommand
        {
            get
            {
                if (_deleteCommand == null)
                {
                    _deleteCommand = new DelegateCommand(DeleteTransaction, CanDeleteTransaction);
                }

                return _deleteCommand;
            }
        }

        /// <summary>
        /// Gets the transactions.
        /// </summary>
        public ObservableCollection<Transaction> Transactions
        {
            get
            {
                if (_transactions == null)
                {
                    RefreshList();
                }

                return _transactions;
            }

            private set
            {
                if (value != _transactions)
                {
                    _transactions = value;

                    RaisePropertyChangedEvent("Transactions");
                }
            }
        }

        /// <summary>
        /// Gets the transactions.
        /// </summary>
        public ObservableCollection<KeyValuePair<int, decimal>> ExpanseChartData
        {
            get
            {
                if (_expanceChartData == null)
                {
                    RefreshList();
                }

                return _expanceChartData;
            }

            private set
            {
                if (value != _expanceChartData)
                {
                    _expanceChartData = value;

                    RaisePropertyChangedEvent("ExpanseChartData");
                }
            }
        }

        /// <summary>
        /// Gets the transactions.
        /// </summary>
        public ObservableCollection<KeyValuePair<TransactionTypes, decimal>> ComparisonChartData
        {
            get
            {
                if (_comparisonChartData == null)
                {
                    RefreshList();
                }

                return _comparisonChartData;
            }

            private set
            {
                if (value != _comparisonChartData)
                {
                    _comparisonChartData = value;

                    RaisePropertyChangedEvent("ComparisonChartData");
                }
            }
        }

        /// <summary>
        /// Gets or sets the selected transaction.
        /// </summary>
        /// <value>
        /// The selected transaction.
        /// </value>
        public Transaction SelectedTransaction
        {
            get
            {
                return _selectedTransaction;
            }

            set
            {
                RaisePropertyChangingEvent("SelectedTransaction");
                _selectedTransaction = value;
                RaisePropertyChangedEvent("SelectedTransaction");
                ((DelegateCommand)EditCommand).RaiseCanExecuteChanged();
                ((DelegateCommand)DeleteCommand).RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        /// <value>
        /// The sort by.
        /// </value>
        public SortByType SortBy
        {
            get
            {
                return _sortBy;
            }

            set
            {
                RaisePropertyChangingEvent("SortBy");
                _sortBy = value;
                RaisePropertyChangedEvent("SortBy");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets the sort order.
        /// </summary>
        /// <value>
        /// The sort order.
        /// </value>
        public OrderByType SortOrder
        {
            get
            {
                return _sortOrder;
            }

            set
            {
                RaisePropertyChangingEvent("SortOrder");
                _sortOrder = value;
                RaisePropertyChangedEvent("SortOrder");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets the selected query accounts.
        /// </summary>
        /// <value>
        /// The selected query accounts.
        /// </value>
        public int SelectedQueryAccount
        {
            get
            {
                return _filterAccountId;
            }

            set
            {
                RaisePropertyChangingEvent("SelectedQueryAccounts");
                _filterAccountId = value;
                RaisePropertyChangedEvent("SelectedQueryAccounts");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets the selected query accounts.
        /// </summary>
        /// <value>
        /// The selected query accounts.
        /// </value>
        public int SelectedQueryTag
        {
            get
            {
                return _filterAccountId;
            }

            set
            {
                RaisePropertyChangingEvent("SelectedQueryTag");
                _filterTagId = value;
                RaisePropertyChangedEvent("SelectedQueryTag");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets the month filter.
        /// </summary>
        /// <value>
        /// The month filter.
        /// </value>
        public int MonthFilter
        {
            get
            {
                return _filterMonth;
            }

            set
            {
                RaisePropertyChangingEvent("MonthFilter");
                _filterMonth = value;
                RaisePropertyChangedEvent("MonthFilter");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [show income].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [show income]; otherwise, <c>false</c>.
        /// </value>
        public bool ShowIncome
        {
            get
            {
                return _showIncome;
            }

            set
            {
                RaisePropertyChangingEvent("ShowIncome");
                _showIncome = value;
                RaisePropertyChangedEvent("ShowIncome");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [show expanse].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [show expanse]; otherwise, <c>false</c>.
        /// </value>
        public bool ShowExpanse
        {
            get
            {
                return _showExpanse;
            }

            set
            {
                RaisePropertyChangingEvent("ShowExpanse");
                _showExpanse = value;
                RaisePropertyChangedEvent("ShowExpanse");
                RefreshList();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is account view.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is account view; otherwise, <c>false</c>.
        /// </value>
        public bool AllowChangeAccount
        {
            get
            {
                return _allowChangeAccount;
            }

            set
            {
                RaisePropertyChangingEvent("AllowChangeAccount");
                _allowChangeAccount = value;
                RaisePropertyChangedEvent("AllowChangeAccount");
            }
        }

        /// <summary>
        /// Refreshes the list.
        /// </summary>
        public void RefreshList()
        {
            if (SelectedQueryAccount > 0)
            {
                var transactionFilter = new Business.Entities.TransactionFilter
                    {
                        SortBy = (Business.Entities.SortByType)SortBy,
                        OrderBy = (Business.Entities.OrderByType)SortOrder,
                        StartDate = GetFilterStartDate(),
                        EndDate = DateTime.Today,
                        IncludeExpanse = ShowExpanse,
                        IncludeIncome = ShowIncome
                    };

                var data =
                    _transactionManager.FilteredList(transactionFilter, SelectedQueryAccount < 0 ? (int?)null : SelectedQueryAccount)
                            .Select(x => x.Map<Business.Entities.Transaction, Transaction>());
                Transactions = new ObservableCollection<Transaction>(data.ToList());

                var expanseChartdata = new Dictionary<int, decimal>();

                foreach (var transaction in data)
                {
                    foreach (var tag in transaction.Tags)
                    {
                        if (expanseChartdata.ContainsKey(tag.TagId))
                        {
                            expanseChartdata[tag.TagId] += transaction.Amount;
                        }
                        else
                        {
                            expanseChartdata.Add(tag.TagId, transaction.Amount);
                        }
                    }
                }

                var comparisonChartdata = Transactions.GroupBy(t => t.TransactionType)
                    .Select(g => new KeyValuePair<TransactionTypes, decimal>(g.Key, g.Sum(s => s.Amount)));
                ExpanseChartData = new ObservableCollection<KeyValuePair<int, decimal>>(expanseChartdata);
                ComparisonChartData =
                    new ObservableCollection<KeyValuePair<TransactionTypes, decimal>>(comparisonChartdata);

                if (!AllowChangeAccount)
                {
                    var account = _accountManager.GetById(SelectedQueryAccount);
                    Title = account == null ? "Transactions" : "Transactions for " + account.Name;
                }
                else
                {
                    Title = "Transactions";
                }
            }
            else
            {
                var transactionFilter = new Business.Entities.TransactionFilter
                {
                    SortBy = (Business.Entities.SortByType)SortBy,
                    OrderBy = (Business.Entities.OrderByType)SortOrder,
                    StartDate = GetFilterStartDate(),
                    EndDate = DateTime.Today,
                    IncludeExpanse = ShowExpanse,
                    IncludeIncome = ShowIncome
                };

                var data = _transactionManager.FilteredListByTag(transactionFilter, _filterTagId)
                        .Select(x => x.Map<Business.Entities.Transaction, Transaction>());
                Transactions = new ObservableCollection<Transaction>(data.ToList());
            }
        }

        /// <summary>
        /// Determines whether this instance [can edit transaction].
        /// </summary>
        /// <returns>
        ///   <c>true</c> if this instance [can edit transaction]; otherwise, <c>false</c>.
        /// </returns>
        public bool CanEditTransaction()
        {
            return SelectedTransaction != null;
        }

        /// <summary>
        /// Edits the transaction.
        /// </summary>
        public void EditTransaction()
        {
            // Initialize
            var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

            // Show Workspace
            var query = new UriQuery { { "ID", SelectedTransaction.TransactionId.ToString(CultureInfo.InvariantCulture) } };
            var transactionEditorUri = new Uri(TransactionModule.TransactionEditor + query, UriKind.Relative);
            regionManager.RequestNavigate(Regions.SecondaryRegion, transactionEditorUri);
        }

        /// <summary>
        /// Determines whether this instance [can delete transaction].
        /// </summary>
        /// <returns>
        ///   <c>true</c> if this instance [can delete transaction]; otherwise, <c>false</c>.
        /// </returns>
        public bool CanDeleteTransaction()
        {
            return SelectedTransaction != null;
        }

        /// <summary>
        /// Deletes the transaction.
        /// </summary>
        public void DeleteTransaction()
        {
            var messageService = ServiceLocator.Current.GetInstance<IMessageBoxService>();
            var session = ServiceLocator.Current.GetInstance<ISession>();

            var result = messageService.Show(
                session.Application.MainWindow,
                "Are you sure you want to delete selected transaction?",
                "Hisaab",
                MessageBoxButton.YesNo,
                MessageBoxImage.Question);

            if (result == MessageBoxResult.Yes)
            {
                _transactionManager.Delete(SelectedTransaction.Map<Transaction, Business.Entities.Transaction>());
                RefreshList();
            }
        }

        /// <summary>
        /// Sorts the specified obj.
        /// </summary>
        /// <param name="obj">The type of sorting.</param>
        public void Sort(object obj)
        {
            SortByType sortBy;
            Enum.TryParse(obj.ToString(), true, out sortBy);
            SortBy = sortBy;
            RefreshList();
        }

        /// <summary>
        /// Orders the by.
        /// </summary>
        /// <param name="obj">The Order sequence</param>
        public void OrderBy(object obj)
        {
            OrderByType sortOrder;
            Enum.TryParse(obj.ToString(), true, out sortOrder);
            SortOrder = sortOrder;
            RefreshList();
        }

        /// <summary>
        /// Filters the by month.
        /// </summary>
        /// <param name="months">The filter amount</param>
        public void FilterByMonth(object months)
        {
            MonthFilter = int.Parse(months.ToString());
            RefreshList();
        }

        /// <summary>
        /// Determines whether this instance [can relate transaction] the specified obj.
        /// </summary>
        /// <param name="obj">The event object</param>
        /// <returns>
        /// <c>true</c> if this instance [can relate transaction] the specified obj; otherwise, <c>false</c>.
        /// </returns>
        public bool CanRelateTransaction(object obj)
        {
            return SelectedTransaction != null;
        }

        #region Implementation of INavigationAware

        /// <summary>
        /// Called when [navigated to].
        /// </summary>
        /// <param name="navigationContext">The navigation context.</param>
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            var accountId = navigationContext.Parameters[TransactionModule.NavigationParameters.Id];
            var tagId = navigationContext.Parameters[TransactionModule.NavigationParameters.TagId];
            if (accountId != null)
            {
                AllowChangeAccount = false;
                SelectedQueryAccount = int.Parse(accountId);
            }
            else if (tagId != null)
            {
                AllowChangeAccount = false;
                _filterAccountId = -1;
                SelectedQueryTag = int.Parse(tagId);
            }
            else
            {
                AllowChangeAccount = true;
            }
        }

        /// <summary>
        /// Determines whether [is navigation target] [the specified navigation context].
        /// </summary>
        /// <param name="navigationContext">The navigation context.</param>
        /// <returns>
        ///   <c>true</c> if [is navigation target] [the specified navigation context]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            var accountId = navigationContext.Parameters["ID"];
            var tagId = navigationContext.Parameters[TransactionModule.NavigationParameters.TagId];

            if (accountId != null)
            {
                return int.Parse(accountId) == _filterAccountId;
            }

            if (tagId != null)
            {
                return int.Parse(tagId) == _filterTagId;
            }

            return false;
        }

        /// <summary>
        /// Called when [navigated from].
        /// </summary>
        /// <param name="navigationContext">The navigation context.</param>
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            GlobalCommands.Refresh.UnregisterCommand(RefreshCommand);
        }

        #endregion Implementation of INavigationAware

        /// <summary>
        /// Invoked whenever the effective value of any dependency property on this <see cref="T:System.Windows.DependencyObject"/> has been updated. The specific dependency property that changed is reported in the event data.
        /// </summary>
        /// <param name="e">Event data that will contain the dependency property identifier of interest, the property metadata for the type, and old and new values.</param>
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            CommandManager.InvalidateRequerySuggested();
            ((DelegateCommand)EditCommand).RaiseCanExecuteChanged();
            ((DelegateCommand)DeleteCommand).RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Called when [active changed].
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnActiveChanged(object sender, EventArgs e)
        {
            if (IsActive)
            {
                TransactionsTabViewModel.Instance.ContextViewModel = this;
                TransactionGlobalCommands.EditTransaction.RegisterCommand(EditCommand);
                TransactionGlobalCommands.DeleteTransaction.RegisterCommand(DeleteCommand);
                TransactionGlobalCommands.RefreshTransaction.RegisterCommand(RefreshCommand);
            }
            else
            {
                TransactionGlobalCommands.EditTransaction.UnregisterCommand(EditCommand);
                TransactionGlobalCommands.DeleteTransaction.UnregisterCommand(DeleteCommand);
                TransactionGlobalCommands.RefreshTransaction.UnregisterCommand(RefreshCommand);
            }
        }

        /// <summary>
        /// Gets the filter start date.
        /// </summary>
        /// <returns>Date Time to which filter date applied</returns>
        private DateTime GetFilterStartDate()
        {
            if (MonthFilter == 0)
            {
                return DateTime.Today.AddDays(-DateTime.Today.Day + 1);
            }

            if (MonthFilter < 0)
            {
                return DateTime.MinValue;
            }

            return DateTime.Today.AddMonths(-MonthFilter);
        }
    }
}